/*
 * File:   Reader.cpp
 * Author: Elevator
 *
 * Created on July 24, 2013, 8:40 PM
 */

#include <stdio.h>

#include "Serialization.h"
#include "Exceptions.h"

namespace data
{
	Scene* DeserializeScene( const Json::Value& value )
	{
		Scene* scene = new Scene(
			value["name"].asString( ),
			value["description"].asString( ),
			value["map"].asString( ),
			DeserializeCamera( value["camera"] ) );

		Json::Value jsonCreatures = value["creatures"];
		for( uint i = 0; i < jsonCreatures.size( ); ++i )
			scene->AddCreature( DeserializeCreature( jsonCreatures[i] ) );

		Json::Value jsonPlayers = value["players"];
		for( uint i = 0; i < jsonPlayers.size( ); ++i )
			scene->AddPlayer( DeserializePlayer( jsonPlayers[i] ) );

		return scene;
	}

	Json::Value SerializeScene( const Scene* scene )
	{
		Json::Value jsonCreatures;
		for( IteratorPtr<const Creature*> creaturesIter( scene->GetCreatures()->GetIterator() ); !creaturesIter->IsEnd( ); creaturesIter->Next( ) )
			jsonCreatures.append( SerializeCreature( creaturesIter->Current( ) ) );

		Json::Value jsonPlayers;
		for( IteratorPtr<Player> playersIter( scene->GetPlayers()->GetIterator() ); !playersIter->IsEnd( ); playersIter->Next( ) )
			jsonPlayers.append( SerializePlayer( playersIter->Current( ) ) );

		Json::Value value;
		value["name"] = scene->GetName();
		value["description"] = scene->GetDescription();
		value["players"] = jsonPlayers;
		value["creatures"] = jsonCreatures;
		value["map"] = scene->GetMapFileName();
		value["camera"] = SerializeCamera(scene->GetCamera());
		return value;
	}

	Camera* DeserializeCamera( const Json::Value& value )
	{
		Transform position = DeserializeTransform( value["position"] );
		Transform velocity = DeserializeTransform( value["velocity"] );
		float scale = value["scale"].asFloat( );

		return new Camera( position, velocity, scale );
	}

	Json::Value SerializeCamera( const Camera* camera )
	{
		Json::Value value;
		value["position"] = SerializeTransform(camera->GetPosition());
		value["velocity"] = SerializeTransform(camera->GetVelocity());
		value["scale"] = camera->GetScale();
        return value;
	}


	Map* DeserializeMap( const Json::Value& value )
	{
		Map* map = new Map(
			value["name"].asString( ),
			value["description"].asString( ) );

		Json::Value jsonObstacles = value["obstacles"];
		for( uint i = 0; i < jsonObstacles.size( ); ++i )
			map->AddObstacle( DeserializeObstacle( jsonObstacles[i] ) );

		return map;
	}

	Json::Value SerializeMap( const Map* map )
	{
		Json::Value jsonObstacles;
		for( IteratorPtr<const Obstacle*> obstaclesIter( map->GetObstacles()->GetIterator() ); !obstaclesIter->IsEnd( ); obstaclesIter->Next( ) )
			jsonObstacles.append( SerializeObstacle( obstaclesIter->Current( ) ) );

		Json::Value value;
		value["name"] = map->GetName();
		value["description"] = map->GetDescription();
		value["obstacles"] = jsonObstacles;
		return value;
	}

	Obstacle* DeserializeObstacle( const Json::Value& value )
	{
		Obstacle* obstacle = new Obstacle(value["id"].asString( ));

		Json::Value jsonShapes = value["shapes"];
		for( uint i = 0; i < jsonShapes.size( ); ++i )
			obstacle->AddShape( DeserializeShape( jsonShapes[i] ) );

		return obstacle;
	}

	Json::Value SerializeObstacle( const Obstacle* obstacle )
	{
		Json::Value jsonShapes;
		for( IteratorPtr<const Shape*> shapesIter( obstacle->GetShapes()->GetIterator() ); !shapesIter->IsEnd( ); shapesIter->Next( ) )
			jsonShapes.append( SerializeShape( shapesIter->Current( ) ) );

		Json::Value value;
		value["id"] = obstacle->GetId();
		value["shapes"] = jsonShapes;
		return value;
	}

	Shape* DeserializeShape( const Json::Value& value )
	{
		Shape* shape = new Shape();
		for( uint i = 0; i < value.size( ); ++i )
			shape->AddVertex( DeserializeCircle( value[i] ) );

		return shape;
	}

	Json::Value SerializeShape(const Shape* shape)
	{
		Json::Value jsonVertices;
		for( IteratorPtr<Circle> verticesIter( shape->GetVertices()->GetIterator() ); !verticesIter->IsEnd( ); verticesIter->Next( ) )
			jsonVertices.append( SerializeCircle( verticesIter->Current( ) ) );

		return jsonVertices;
	}

	Player DeserializePlayer( const Json::Value& value )
	{
		return
			Player(
			   DeserializePlayerType( value["type"] ),
			   value["name"].asString( ),
			   value["creature"].asString( )
			);
	}

	Json::Value SerializePlayer( const Player& player )
	{
		Json::Value value;
		value["type"] = SerializePlayerType(player.GetType());
		value["name"] = player.GetName();
		value["creature"] = player.GetCreatureId();
		return value;
	}

	PlayerType DeserializePlayerType( const Json::Value & value )
	{
		return StringToPlayerType( value.asString( ) );
	}

	Json::Value SerializePlayerType( const PlayerType& playerType )
	{
		return Json::Value(PlayerTypeToString(playerType));
	}


	Creature* DeserializeCreature( const Json::Value & value )
	{
		return new Creature(
			value["id"].asString( ),
			DeserializeTransform( value["position"] ),
			value["definition"].asString( ) );
	}

	Json::Value SerializeCreature( const Creature* creature )
	{
		Json::Value value;
		value["id"] = creature->GetId();
		value["position"] = SerializeTransform( creature->GetPosition() );
		value["definition"] = creature->GetDefinitionFileName();
		return value;
	}

	CreatureDef* DeserializeCreatureDef( const Json::Value & value )
	{
		CreatureDef* creatureDef = new CreatureDef(
			value["name"].asString( ),
			value["description"].asString( ),
			value["head"].asString( ) );

		Json::Value jsonDetails = value["details"];
		for( uint i = 0; i < jsonDetails.size( ); ++i )
			creatureDef->AddDetail( DeserializeDetail( jsonDetails[i] ) );

		Json::Value jsonRotors = value["rotors"];
		for( uint i = 0; i < jsonRotors.size( ); ++i )
			creatureDef->AddRotor( DeserializeRotor( jsonRotors[i] ) );

		Json::Value jsonLinks = value["links"];
		for( uint i = 0; i < jsonLinks.size( ); ++i )
			creatureDef->AddLink( DeserializeLink( jsonLinks[i] ) );

		return creatureDef;
	}

	Json::Value SerializeCreatureDef( const CreatureDef* creatureDef )
	{
		Json::Value jsonDetails;
		for( IteratorPtr<const Detail*> detailsIter( creatureDef->GetDetails()->GetIterator() ); !detailsIter->IsEnd( ); detailsIter->Next( ) )
			jsonDetails.append( SerializeDetail( detailsIter->Current( ) ) );

		Json::Value jsonRotors;
		for( IteratorPtr<const Rotor*> rotorsIter( creatureDef->GetRotors()->GetIterator() ); !rotorsIter->IsEnd( ); rotorsIter->Next( ) )
			jsonRotors.append( SerializeRotor( rotorsIter->Current( ) ) );

		Json::Value jsonLinks;
		for( IteratorPtr<Link> linksIter( creatureDef->GetLinks()->GetIterator() ); !linksIter->IsEnd( ); linksIter->Next( ) )
			jsonLinks.append( SerializeLink( linksIter->Current( ) ) );

		Json::Value value;
		value["name"] = creatureDef->GetName();
		value["description"] = creatureDef->GetDescription();
		value["head"] = creatureDef->GetHeadId();
		value["details"] = jsonDetails;
		value["rotors"] = jsonRotors;
		value["links"] = jsonLinks;
		return value;
	}

	Detail* DeserializeDetail( const Json::Value & value )
	{
		Detail* detail = new Detail(
			value["id"].asString( ),
			DeserializeTransform( value["position"] ),
			value["definition"].asString( ));

		Json::Value jsonRules = value["rules"];
		for( uint i = 0; i < jsonRules.size( ); ++i )
			detail->AddRule( DeserializeRule( jsonRules[i] ) );

		return detail;
	}

	Json::Value SerializeDetail( const Detail* detail )
	{
		Json::Value jsonRules;
		for( IteratorPtr<Rule> rulesIter( detail->GetRules()->GetIterator() ); !rulesIter->IsEnd( ); rulesIter->Next( ) )
			jsonRules.append( SerializeRule( rulesIter->Current( ) ) );

		Json::Value value;
		value["id"] = detail->GetId();
		value["position"] = SerializeTransform( detail->GetPosition() );
		value["definition"] = detail->GetDefinitionFileName();
		value["rules"] = jsonRules;
		return value;
	}

	DetailDef* DeserializeDetailDef( const Json::Value & value )
	{
		DetailDef* detailDef = new DetailDef(
			value["name"].asString( ),
			value["description"].asString( ),
			DeserializeTexture(value["texture"]),
			value["mass"].asFloat( ),
			value["inertia"].asFloat( ),
			value["friction"].asFloat( ),
			value["restitution"].asFloat( )
		);

		Json::Value jsonVertices = value["vertices"];
		for( uint i = 0; i < jsonVertices.size( ); ++i )
			detailDef->AddVertex( DeserializeCircle( jsonVertices[i] ) );

		return detailDef;
	}

	Json::Value SerializeDetailDef( const DetailDef* detailDef )
	{
		Json::Value jsonVertices;
		for( IteratorPtr<Circle> verticesIter( detailDef->GetVertices()->GetIterator() ); !verticesIter->IsEnd( ); verticesIter->Next( ) )
			jsonVertices.append( SerializeCircle( verticesIter->Current( ) ) );

		Json::Value value;
		value["name"] = detailDef->GetName();
		value["description"] = detailDef->GetDescription();
		value["texture"] = SerializeTexture(detailDef->GetTexture());
		value["mass"] = detailDef->GetMass();
		value["inertia"] = detailDef->GetInertia();
		value["friction"] = detailDef->GetFriction();
		value["restitution"] = detailDef->GetRestitution();
		value["vertices"] = jsonVertices;

		return value;
	}


	Texture DeserializeTexture( const Json::Value & value )
	{
		return Texture(
			value["file"].asString( ),
			DeserializeTransform(value["position"]),
			value["width"].asFloat( ),
			value["height"].asFloat( )
		);
	}

	Json::Value SerializeTexture( const Texture& texture )
	{
		Json::Value value;
		value["file"] = texture.GetFileName();
		value["position"] = SerializeTransform(texture.GetPosition());
		value["width"] = texture.GetWidth();
		value["height"] = texture.GetHeight();
		return value;
	}

	Rotor* DeserializeRotor( const Json::Value & value )
	{
		Rotor* rotor = new Rotor(
			value["id"].asString( ),
			value["detailA"].asString( ),
			value["detailB"].asString( ),
			DeserializeVec2( value["position"] ),
			value["definition"].asString( )
		);

		Json::Value jsonRules = value["rules"];
		for( uint i = 0; i < jsonRules.size( ); ++i )
			rotor->AddRule( DeserializeRule( jsonRules[i] ) );

		return rotor;
	}

	Json::Value SerializeRotor( const Rotor* rotor )
	{
		Json::Value jsonRules;
		for( IteratorPtr<Rule> rulesIter( rotor->GetRules()->GetIterator() ); !rulesIter->IsEnd( ); rulesIter->Next( ) )
			jsonRules.append( SerializeRule( rulesIter->Current( ) ) );

		Json::Value value;
		value["id"] = rotor->GetId();
		value["detailA"] = rotor->GetDetailAId();
		value["detailB"] = rotor->GetDetailBId();
		value["position"] = SerializeVec2(rotor->GetPosition());
		value["definition"] = rotor->GetDefinitionFileName();
		value["rules"] = jsonRules;

		return value;
	}


	RotorDef* DeserializeRotorDef( const Json::Value & value )
	{
		return
			new RotorDef(
				value["name"].asString( ),
				value["description"].asString( ),
				value["torque"].asFloat( )
			);
	}

	Json::Value SerializeRotorDef( const RotorDef* rotorDef )
	{
		Json::Value value;
		value["name"] = rotorDef->GetName();
		value["description"] = rotorDef->GetDescription();
		value["torque"] = rotorDef->GetTorque();
		return value;
	}


	Link DeserializeLink( const Json::Value & value )
	{
		return Link( value["partA"].asString( ), value["partB"].asString( ) );
	}

	Json::Value SerializeLink( const Link& link )
	{
		Json::Value value;
		value["partA"] = link.GetPartAId();
		value["partB"] = link.GetPartBId();
		return value;
	}

	Rule DeserializeRule( const Json::Value & value )
	{
		return
			Rule(
				DeserializeCommand( value["command"] ),
				DeserializeAction( value["action"] )
			);
	}

	Json::Value SerializeRule( const Rule& rule )
	{
		Json::Value value;
		value["command"] = SerializeCommand(rule.GetCommand());
		value["action"] = SerializeAction(rule.GetAction());
		return value;
	}


	Command DeserializeCommand( const Json::Value & value )
	{
		return
			Command(
				value["key"].asString( )[0],
				DeserializeCommandType( value["type"].asString( ) )
			);
	}

	Json::Value SerializeCommand( const Command& command )
	{
		Json::Value value;
		value["key"] = (char)command.GetKey();
		value["type"] = SerializeCommandType(command.GetType());
		return value;
	}


	CommandType DeserializeCommandType( const Json::Value & value )
	{
		return StringToCommandType( value.asString( ) );
	}

	Json::Value SerializeCommandType( const CommandType& commandType )
	{
		return Json::Value(CommandTypeToString(commandType));
	}


	Action DeserializeAction( const Json::Value & value )
	{
		return StringToAction( value.asString( ) );
	}

	Json::Value SerializeAction( const Action & action )
	{
		return Json::Value(ActionToString(action));
	}


	Circle DeserializeCircle( const Json::Value & value )
	{
		return Circle( DeserializeVec2( value["position"] ), value["radius"].asFloat( ) );
	}

	Json::Value SerializeCircle( const Circle & circle)
	{
		Json::Value value;
		value["position"] = SerializeVec2(circle.Center);
		value["radius"] = circle.Radius;
		return value;
	}


	Transform DeserializeTransform( const Json::Value & value )
	{
		return Transform( DeserializeVec2( value["vector"] ), value["rotation"].asFloat( ) );
	}

	Json::Value SerializeTransform( const Transform & transform )
	{
		Json::Value value;
		value["vector"] = SerializeVec2(transform.Vec);
		value["rotation"] = transform.Rot;
		return value;
	}


	Vec2 DeserializeVec2( const Json::Value & value )
	{
		return Vec2( value["x"].asFloat( ), value["y"].asFloat( ) );
	}

	Json::Value SerializeVec2( const Vec2 & vec2 )
	{
		Json::Value value;
		value["x"] = vec2.X;
		value["y"] = vec2.Y;
		return value;
	}
}
